#define _GNU_SOURCE
#include "app_device.h"
#include "log/log.h"
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "app_pool.h"
#include "app_com.h"

#define BUFFER_SIZE 1024

/**
 * 向设备写数据的任务函数: 在线程池中的某个线程中执行
 */
static int write_task_fun(void *arg)
{
    Device *device = (Device *)arg;
    // 从设备的写缓存中读取出第一份完整数据作为消息数据准备发送
    char data[128];
    app_buffer_read(device->write_buffer, data, 3); // conn_type id_len msg_len
    app_buffer_read(device->write_buffer, data + 3, data[1] + data[2]);
    int len = 3 + data[1] + data[2];
    // 在写入设备前调用写的前置处理函数
    if (device->funs->pre_write)
    {
        len = device->funs->pre_write(device, data, len);
        if (len > 0)
        {
            // 得到当前时间与上一次写入时间的时间差
            long time_distance = app_com_getCurrentTime() - device->last_write_time;
            // 如果时间差小于100毫秒，则等待
            if (time_distance < 1000)
            {
                usleep(100 - time_distance) * 1000;
            }

            // 写入设备
            write(device->fd, data, len);
            device->last_write_time = app_com_getCurrentTime();
            return 0;
        }
    }
    return -1;
}

/**
 * 发送消息的任务函数: 在线程池中的某个线程中执行
 */
static int send_task_fun(void *arg)
{
    Device *device = (Device *)arg;
    // 从读缓存中读取出第一份完整数据作为消息数据准备发送
    char data[128];
    app_buffer_read(device->read_buffer, data, 3); // conn_type id_len msg_len
    app_buffer_read(device->read_buffer, data + 3, data[1] + data[2]);
    int len = 3 + data[1] + data[2];
    // 发送消息数据给服务器
    if (device->funs->send_msg)
    {
        device->funs->send_msg(device, data, len);
    }
}

/**
 * 不断读取设备数据并发送给服务器的线程
 */
static void *read_thread_fun(void *arg)
{
    Device *device = (Device *)arg;
    char data[128];
    while (device->running == 1)
    {
        // 重置容器
        memset(data, 0, sizeof(data));
        // 不断读取设备数据
        int len = read(device->fd, data, sizeof(data)); // 阻塞式读取
        // 调用post_read函数对读取到的data的进行后置处理
        if (device->funs->post_read)
        {
            len = device->funs->post_read(device, data, len);
            // 只有处理后的数据长度大于0时才进行处理
            if (len > 0)
            {
                //  将读取到的处理后的数据保存到读缓存
                app_buffer_write(device->read_buffer, data, len);

                // 注册发送消息的任务
                app_pool_registerTask(send_task_fun, device);
            }
        }
    }
}

/**
 * 初始化设备
 */
Device *app_device_init(char *filname)
{
    Device *device = (Device *)malloc(sizeof(Device));

    device->filname = filname;
    device->fd = open(filname, O_CREAT | O_RDWR, 0644);
    if (device->fd < 0)
    {
        log_error("open %s failed", filname);
        return NULL;
    }
    device->conn_type = CONN_NONE;
    device->dev_type = DEV_GENERAL;
    device->read_buffer = app_buffer_init(BUFFER_SIZE);
    device->write_buffer = app_buffer_init(BUFFER_SIZE);
    device->running = 0;
    device->funs = malloc(sizeof(struct DeviceFuns));
    device->last_write_time = 0;

    return device;
}

/**
 * 启动设备
 */
int app_device_start(Device *device)
{
    if (device->running == 1)
    {
        log_debug("device is already running");
        return 0;
    }
    // 标识线程已运行
    device->running = 1;
    // 创建启动线程
    if (pthread_create(&device->read_thread, NULL, read_thread_fun, device) != 0)
    {
        log_error("create read thread failed");
        device->running = 0;
        return -1;
    }

    return 0;
}
/**
 * 停止设备
 */
int app_device_stop(Device *device)
{
    if (device->running == 0)
    {
        log_debug("device is already stop");
        return 0;
    }
    pthread_cancel(device->read_thread);
    pthread_join(device->read_thread, NULL);
    device->running = 0;

    return 0;
}
/**
 * 关闭（释放）设备
 */
void app_device_close(Device *device)
{
    app_buffer_free(device->read_buffer);
    app_buffer_free(device->write_buffer);

    free(device->funs);

    free(device);
}

/**
 * 接收处理数据
 */
int app_device_recvMessage(Device *device, char *data, int len)
{
    // 将接收的消息数据保存到设备写缓存中
    app_buffer_write(device->write_buffer, data, len);
    // 注册一个写任务来完成后缀将数据写入设备的操作

    return app_pool_registerTask(write_task_fun, device);
}